import element.synt.MainFuncDef;
import intermediate_representation.*;
import mips.Function;
import mips.Reg;
import mips.directive.Asciiz;
import mips.directive.Space;
import mips.element.Instruction;
import mips.element.MipsElement;
import mips.element.Section;
import mips.instruction.*;
import symbal.Symbal;
import symbal.SymbalTable;
import symbal.Vtype;
import symbal.mipssymbal.MipsSymbal;
import table.Grf;
import table.MipsTable;

import java.util.ArrayList;

public class GenerateMips {
    private ArrayList<InterRP> intercode;
    private int pointer;
    private InterRP nowcode;
    private int stackindex;

    public GenerateMips(ArrayList<InterRP> intercode) {
        this.intercode = intercode;
        this.intercode.add(new EndRP());
        this.pointer = 0;
    }

    public String toString() {
        return MipsTable.toPrint();
    }

    public void getintercode() {
        nowcode = intercode.get(pointer);
        pointer++;
    }

    public boolean isNumber(String con) {
        for (int i = 0; i < con.length(); i++) {
            if (i == 0 && con.charAt(i) == '-') {
                continue;
            }
            if (!Character.isDigit(con.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    public void pushStack() {
        stackindex += 4;
    }

    public void pushStack(int length) {
        stackindex += 4 * length;
    }

    public void goDown() {
        MipsTable.mipsTable = new SymbalTable(MipsTable.mipsTable);
    }

    public void goUp() {
        MipsTable.mipsTable = MipsTable.mipsTable.getFather();
    }

    public Reg findRega() {
        for (Reg reg1: Grf.regs_attr) {
            if (reg1.isFree()) {
                reg1.occupy();
                return reg1;
            }
        }
        return null;
    }

    public void generate() {
        Grf.Init();
        getintercode();
        generate_data();
        while (!(nowcode instanceof EndRP)) {
            generate_function();
        }
        LI li = new LI(Grf.v0, 10);
        SYSCALL syscall = new SYSCALL();
        MipsTable.meanfunction.addEnding(li);
        MipsTable.meanfunction.addEnding(syscall);
        JR jr = new JR(Grf.ra);
        for (Function function: MipsTable.functions) {
            function.addEnding(jr);
        }
    }

    public String getParaName(String para) {
        int len = para.length();
        for (int i = 0; i < len; i++) {
            if (para.charAt(i) == '@' || para.charAt(i) == '[') {
                return para.substring(0, i);
            }
        }
        return para;
    }

    public String getParaIndex(String para) {
        int len = para.length();
        int begin = 0, end = 0;
        for (int i = 0; i < len; i++) {
            if (para.charAt(i) == '[') {
                begin = i;
            } else if (para.charAt(i) == ']') {
                end = i;
            }
        }
        if (begin == 0 && end == 0) {
            return null;
        }
        return para.substring(begin + 1, end);
    }

    public ArrayList<String> sapaString(String formatString) {
        int begin = 1;
        int end = 1;
        ArrayList<String> units = new ArrayList<>();
        for (int i = 1; i < formatString.length() - 1; i++) {
            if (formatString.charAt(i) == '%') {
                if (i != 1) {
                    end = i;
                    String unit = formatString.substring(begin, end);
                    units.add(unit);
                }
                units.add("%d");
                begin = i + 2;
            }
        }
        end = formatString.length() - 1;
        String unit = formatString.substring(begin, end);
        if (unit.length() > 0) {
            units.add(unit);
        }
        return units;
    }

    public void generate_data() {
        while (!(nowcode instanceof FuncDefRP)) {
            if (nowcode instanceof DefineRP) {
                boolean isConst = ((DefineRP) nowcode).isIsconst();
                Vtype type = ((DefineRP) nowcode).getType();
                String name = ((DefineRP) nowcode).getName();

                if (type.equals(Vtype.INT)) {
                    Space space = new Space(name, 4);
                    MipsTable._data.add(space);
                    MipsSymbal symbal = new MipsSymbal(name, Vtype.INT);
                    symbal.setSpaceLocation(space);
                    MipsTable.mipsTable.addSymbal(symbal);
                } else if (type.equals(Vtype.INT1)) {
                    int len1 = ((DefineRP) nowcode).getLen1();
                    Space space = new Space(name, 4 * len1);
                    MipsTable._data.add(space);
                    MipsSymbal symbal = new MipsSymbal(name, Vtype.INT1);
                    symbal.setSpaceLocation(space);
                    MipsTable.mipsTable.addSymbal(symbal);
                } else if (type.equals(Vtype.INT2)) {
                    int len1 = ((DefineRP) nowcode).getLen1();
                    int len2 = ((DefineRP) nowcode).getLen2();
                    Space space = new Space(name, 4 * len1 * len2);
                    MipsTable._data.add(space);
                    MipsSymbal symbal = new MipsSymbal(name, Vtype.INT2);
                    symbal.setSpaceLocation(space);
                    MipsTable.mipsTable.addSymbal(symbal);
                }
            } else if (nowcode instanceof AssignRP) {
                String result = ((AssignRP) nowcode).getResult();
                String source = ((AssignRP) nowcode).getSource();
                int value = Integer.parseInt(source);
                String name = ((AssignRP) nowcode).getResultName();
                MipsSymbal symbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(name);
                if (symbal.getVtype().equals(Vtype.INT)) {
                    if (symbal.onData()) {
                        Space space = symbal.getSpaceLocation();
                        String label = space.getName();
                        LI assign = new LI(Grf.t0, value);
                        SW save = new SW(Grf.t0, label, Grf.zero);
                        MipsTable._globalinit.add(assign);
                        MipsTable._globalinit.add(save);
                    }
                } else {
                    if (symbal.onData()) {
                        Space space = symbal.getSpaceLocation();
                        String label = space.getName();
                        if (intercode.get(pointer - 2) instanceof DefineRP) {
                            LA tospace = new LA(Grf.s0, label);
                            MipsTable._globalinit.add(tospace);
                        }
                        LI assign = new LI(Grf.t0, value);
                        int offset = Integer.parseInt(((AssignRP) nowcode).getResultIndex()) * 4;
                        SW save = new SW(Grf.t0, String.valueOf(offset), Grf.s0);
                        MipsTable._globalinit.add(assign);
                        MipsTable._globalinit.add(save);
                    }
                }
            }
            getintercode();
        }
    }

    public ArrayList<InterRP> separete_function() {
        ArrayList<InterRP> function_section = new ArrayList<>();
        if (nowcode instanceof FuncDefRP) {
            function_section.add(nowcode);
            getintercode();
        }
        while (!(nowcode instanceof FuncDefRP) && !(nowcode instanceof EndRP)) {
            function_section.add(nowcode);
            getintercode();
        }
        return function_section;
    }

    public void generate_function() {
        ArrayList<InterRP> function_section = separete_function();
        FuncDefRP funcDefRP = (FuncDefRP) function_section.get(0);
        String funcname = funcDefRP.getName();
        if (funcname.equals("main")) {
            MipsTable.nowfunction = MipsTable.meanfunction;
        } else {
            MipsTable.putfunction(funcname);
        }
        Vtype returntype = funcDefRP.getReturntype();
        ArrayList<ParaRP> paras = funcDefRP.getParaRPS();
        MipsSymbal funcsymbal = new MipsSymbal(funcname, Vtype.FUNC);
        MipsTable.mipsTable.addSymbal(funcsymbal);

        MOVE move = new MOVE(Grf.fp, Grf.sp);
        MipsTable.nowfunction.addPrepare(move);

        goDown();

        int paranum = 0;
        int savenum = 0;
        for (ParaRP interRP: paras) {
            String name = ((ParaRP) interRP).getName();
            Vtype vtype = ((ParaRP) interRP).getType();
            if (vtype.equals(Vtype.INT)) {
                MipsSymbal symbal = new MipsSymbal(name, vtype);
                symbal.setSpLocation(savenum);
                MipsTable.mipsTable.addSymbal(symbal);
                LW lw = new LW(Grf.v0, String.valueOf(paranum), Grf.fp);
                SW sw = new SW(Grf.v0, String.valueOf(savenum), Grf.sp);
                MipsTable.nowfunction.addInit(lw);
                MipsTable.nowfunction.addInit(sw);
                savenum += 4;
                paranum += 4;
            } else {
                MipsSymbal symbal = new MipsSymbal(name, vtype);
                symbal.setDirectLocation(savenum);
                if (vtype.equals(Vtype.INT2)) {
                    int len2 = ((ParaRP) interRP).getLen2();
                    symbal.setLen2(len2);
                }
                MipsTable.mipsTable.addSymbal(symbal);
                LW lw = new LW(Grf.v0, String.valueOf(paranum), Grf.fp);
                SW sw = new SW(Grf.v0, String.valueOf(savenum), Grf.sp);
                MipsTable.nowfunction.addInit(lw);
                MipsTable.nowfunction.addInit(sw);
                savenum += 4;
                paranum += 4;
            }
        }
        pushStack(savenum / 4);


        for (int i = 0; i < function_section.size(); i++) {
            InterRP interRP = function_section.get(i);
            if (interRP instanceof ParaRP) {
                Vtype vtype = ((ParaRP) interRP).getType();
                if (vtype.equals(Vtype.INT)) {
                    pushStack();
                }
            }
            if (interRP instanceof AssignRP) {
                generate_assign((AssignRP) interRP, i);
            } else if (interRP instanceof DefineRP) {
                generate_define((DefineRP) interRP, i);
            } else if (interRP instanceof ExpRP) {
                generate_exp((ExpRP) interRP, i);
            } else if (interRP instanceof CallRP) {
                generate_call((CallRP) interRP, i);
            } else if (interRP instanceof GetintRP) {
                generate_getint((GetintRP) interRP, i);
            } else if (interRP instanceof PrintRP) {
                generate_printf((PrintRP) interRP, i);
            } else if (interRP instanceof ReturnRP) {
                generate_return((ReturnRP) interRP, i);
            } else if (interRP instanceof LabelRP) {
                generate_label((LabelRP) interRP, i);
            } else if (interRP instanceof JumpRP) {
                generate_jump((JumpRP) interRP, i);
            } else if (interRP instanceof CjumpRP) {
                generate_cjump((CjumpRP) interRP, i);
            }
            Grf.releaseAll();
        }

        ADDI addi = new ADDI(Grf.sp, Grf.sp, -stackindex);
        MipsTable.nowfunction.addPrepare(addi);

        ADDI addi1 = new ADDI(Grf.sp, Grf.sp, stackindex);
        MipsTable.nowfunction.addEnding(addi1);

        stackindex = 0;

        goUp();
    }

    public void generate_label(LabelRP labelRP, int i) {
        String name = labelRP.getLabel();
        Section section = new Section(name);
        MipsTable.nowfunction.addState(section);
    }

    public void generate_jump(JumpRP jumpRP, int i) {
        String label = jumpRP.getLabel();
        J j = new J(label);
        MipsTable.nowfunction.addState(j);
    }

    public void generate_cjump(CjumpRP cjumpRP, int i) {
        String op1 = cjumpRP.getOp1();
        String op2 = cjumpRP.getOp2();
        String calcu = cjumpRP.getCalcu();
        String label = cjumpRP.getLabel();
        if (isNumber(op1)) {
            int imm = Integer.parseInt(op1);
            MipsSymbal symbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(op2);
            Reg reg = extract(symbal);
            if (imm == 0) {
                if (calcu.equals("beq")) {
                    BEQZ beqz = new BEQZ(reg, label);
                    MipsTable.nowfunction.addState(beqz);
                } else if (calcu.equals("bne")) {
                    BNEZ bnez = new BNEZ(reg, label);
                    MipsTable.nowfunction.addState(bnez);
                } else if (calcu.equals("bgt")) {
                    BLTZ bltz = new BLTZ(reg, label);
                    MipsTable.nowfunction.addState(bltz);
                } else if (calcu.equals("bge")) {
                    BLEZ blez = new BLEZ(reg, label);
                    MipsTable.nowfunction.addState(blez);
                } else if (calcu.equals("blt")) {
                    BGTZ bgtz = new BGTZ(reg, label);
                    MipsTable.nowfunction.addState(bgtz);
                } else if (calcu.equals("ble")) {
                    BGEZ bgez = new BGEZ(reg, label);
                    MipsTable.nowfunction.addState(bgez);
                }
            } else {
                if (calcu.equals("beq")) {
                    BEQ beq = new BEQ(reg, imm, label);
                    MipsTable.nowfunction.addState(beq);
                } else if (calcu.equals("bne")) {
                    BNE bne = new BNE(reg, imm, label);
                    MipsTable.nowfunction.addState(bne);
                } else if (calcu.equals("bgt")) {
                    BLT blt = new BLT(reg, imm, label);
                    MipsTable.nowfunction.addState(blt);
                } else if (calcu.equals("bge")) {
                    BLE ble = new BLE(reg, imm, label);
                    MipsTable.nowfunction.addState(ble);
                } else if (calcu.equals("blt")) {
                    BGT bgt = new BGT(reg, imm, label);
                    MipsTable.nowfunction.addState(bgt);
                } else if (calcu.equals("ble")) {
                    BGE bge = new BGE(reg, imm, label);
                    MipsTable.nowfunction.addState(bge);
                }
            }
        } else if (isNumber(op2)) {
            int imm = Integer.parseInt(op2);
            MipsSymbal symbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(op1);
            Reg reg = extract(symbal);
            if (imm == 0) {
                if (calcu.equals("beq")) {
                    BEQZ beqz = new BEQZ(reg, label);
                    MipsTable.nowfunction.addState(beqz);
                } else if (calcu.equals("bne")) {
                    BNEZ bnez = new BNEZ(reg, label);
                    MipsTable.nowfunction.addState(bnez);
                } else if (calcu.equals("bgt")) {
                    BGTZ bgtz = new BGTZ(reg, label);
                    MipsTable.nowfunction.addState(bgtz);
                } else if (calcu.equals("bge")) {
                    BGEZ bgez = new BGEZ(reg, label);
                    MipsTable.nowfunction.addState(bgez);
                } else if (calcu.equals("blt")) {
                    BLTZ bltz = new BLTZ(reg, label);
                    MipsTable.nowfunction.addState(bltz);
                } else if (calcu.equals("ble")) {
                    BLEZ blez = new BLEZ(reg, label);
                    MipsTable.nowfunction.addState(blez);
                }
            } else {
                if (calcu.equals("beq")) {
                    BEQ beq = new BEQ(reg, imm, label);
                    MipsTable.nowfunction.addState(beq);
                } else if (calcu.equals("bne")) {
                    BNE bne = new BNE(reg, imm, label);
                    MipsTable.nowfunction.addState(bne);
                } else if (calcu.equals("bgt")) {
                    BGT bgt = new BGT(reg, imm, label);
                    MipsTable.nowfunction.addState(bgt);
                } else if (calcu.equals("bge")) {
                    BGE bge = new BGE(reg, imm, label);
                    MipsTable.nowfunction.addState(bge);
                } else if (calcu.equals("blt")) {
                    BLT blt = new BLT(reg, imm, label);
                    MipsTable.nowfunction.addState(blt);
                } else if (calcu.equals("ble")) {
                    BLE ble = new BLE(reg, imm, label);
                    MipsTable.nowfunction.addState(ble);
                }
            }
        } else {
            MipsSymbal symbal1 = (MipsSymbal) MipsTable.mipsTable.findSymbal(op1);
            Reg reg1 = extract(symbal1);
            MipsSymbal symbal2 = (MipsSymbal) MipsTable.mipsTable.findSymbal(op2);
            Reg reg2 = extract(symbal2);
            if (calcu.equals("beq")) {
                BEQ beq = new BEQ(reg1, reg2, label);
                MipsTable.nowfunction.addState(beq);
            } else if (calcu.equals("bne")) {
                BNE bne = new BNE(reg1, reg2, label);
                MipsTable.nowfunction.addState(bne);
            } else if (calcu.equals("bgt")) {
                BGT bgt = new BGT(reg1, reg2, label);
                MipsTable.nowfunction.addState(bgt);
            } else if (calcu.equals("bge")) {
                BGE bge = new BGE(reg1, reg2, label);
                MipsTable.nowfunction.addState(bge);
            } else if (calcu.equals("blt")) {
                BLT blt = new BLT(reg1, reg2, label);
                MipsTable.nowfunction.addState(blt);
            } else if (calcu.equals("ble")) {
                BLE ble = new BLE(reg1, reg2, label);
                MipsTable.nowfunction.addState(ble);
            }
        }
    }

    public void generate_assign(AssignRP interRP, int i) {
        String result = interRP.getResult();
        String source = interRP.getSource();
        String resultname = interRP.getResultName();
        if (MipsTable.mipsTable.checkSymbals(resultname)) {
            MipsSymbal resultSymbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(resultname);
            String resultindex = interRP.getResultIndex();
            if (isNumber(source)) {
                int sourcenum = Integer.parseInt(source);
                assign_symbal_num(resultSymbal, sourcenum, resultindex);
            } else {
                String sourcename = interRP.getSourceName();
                MipsSymbal sourceSymbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(sourcename);
                String sourceindex = interRP.getSourceIndex();
                assign_symbal_symbal(resultSymbal, sourceSymbal, resultindex, sourceindex);
            }
        } else {
            MipsSymbal resultSymbal = new MipsSymbal(resultname, Vtype.INT);
            if (resultname.charAt(0) == '$') {
                int number = Integer.parseInt(resultname.substring(4));
                if (number < 10) {
                    resultSymbal.setRegLocation(Grf.regs_temp.get(number));
                } else {
                    resultSymbal.setSpLocation(stackindex);
                    pushStack();
                }
                MipsTable.mipsTable.addSymbal(resultSymbal);
                generate_assign(interRP, i);
            } else {
                System.out.println(resultname);
                System.out.println("ERROR_a");
            }
        }
    }

    public void generate_define(DefineRP interRP, int i) {
        boolean isconst = interRP.isIsconst();
        String name = interRP.getName();
        Vtype type = interRP.getType();
        if (isconst) {
            if (type.equals(Vtype.INT)) {
                Space space = new Space(name, 4);
                MipsTable._data.add(space);
                MipsSymbal symbal = new MipsSymbal(name, Vtype.INT);
                symbal.setSpaceLocation(space);
                MipsTable.mipsTable.addSymbal(symbal);
            } else if (type.equals(Vtype.INT1)) {
                int len1 = interRP.getLen1();
                Space space = new Space(name, 4 * len1);
                MipsTable._data.add(space);
                MipsSymbal symbal = new MipsSymbal(name, Vtype.INT1);
                symbal.setSpaceLocation(space);
                MipsTable.mipsTable.addSymbal(symbal);
            } else if (type.equals(Vtype.INT2)) {
                int len1 = interRP.getLen1();
                int len2 = interRP.getLen2();
                Space space = new Space(name, 4 * len1 * len2);
                MipsTable._data.add(space);
                MipsSymbal symbal = new MipsSymbal(name, Vtype.INT2);
                symbal.setSpaceLocation(space);
                MipsTable.mipsTable.addSymbal(symbal);
            }
        } else {
            if (type.equals(Vtype.INT)) {
                MipsSymbal symbal = new MipsSymbal(name, Vtype.INT);
                symbal.setSpLocation(stackindex);
                MipsTable.mipsTable.addSymbal(symbal);
                pushStack();
            } else if (type.equals(Vtype.INT1)) {
                int len1 = interRP.getLen1();
                MipsSymbal symbal = new MipsSymbal(name, Vtype.INT1);
                symbal.setSpLocation(stackindex);
                MipsTable.mipsTable.addSymbal(symbal);
                pushStack(len1);
            } else if (type.equals(Vtype.INT2)) {
                int len1 = interRP.getLen1();
                int len2 = interRP.getLen2();
                MipsSymbal symbal = new MipsSymbal(name, Vtype.INT2);
                symbal.setSpLocation(stackindex);
                MipsTable.mipsTable.addSymbal(symbal);
                pushStack(len1 * len2);
            }
        }
    }

    public void generate_exp(ExpRP interRP, int i) {
        String op1 = interRP.getOp1();
        String op = interRP.getOp();
        String result = interRP.getResult();
        if (!MipsTable.mipsTable.checkSymbals(result)) {
            MipsSymbal resultSymbal = new MipsSymbal(result, Vtype.INT);
            if (result.charAt(0) == '$') {
                int number = Integer.parseInt(result.substring(4));
                if (number < 10) {
                    resultSymbal.setRegLocation(Grf.regs_temp.get(number));
                } else {
                    resultSymbal.setSpLocation(stackindex);
                    pushStack();
                }
                MipsTable.mipsTable.addSymbal(resultSymbal);
                generate_exp(interRP, i);
            } else {
                System.out.println(result);
                System.out.println("ERROR_a");
            }
            return;
        }
        if (interRP.getOp2() == null) {
            if (op.equals("+")) {
                generate_posi(op1, result);
            } else if (op.equals("-")) {
                generate_nega(op1, result);
            } else if (op.equals("!")) {
                generate_not(op1, result);
            }
        } else {
            String op2 = interRP.getOp2();
            MipsSymbal resultSymbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(result);
            if (isNumber(op1)) {
                int num = Integer.parseInt(op1);
                MipsSymbal op2Symbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(op2);
                Reg reg = extract(op2Symbal);
                if (resultSymbal.onReg()) {
                    generate_num_reg_cal(resultSymbal.getRegLocation(), num, reg, op);
                } else {
                    Reg reg1 = findRega();
                    generate_num_reg_cal(reg1, num, reg, op);
                    insert(resultSymbal, reg1);
                }
            } else if (isNumber(op2)) {
                int num = Integer.parseInt(op2);
                MipsSymbal op1Symbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(op1);
                Reg reg = extract(op1Symbal);
                if (resultSymbal.onReg()) {
                    generate_reg_num_cal(resultSymbal.getRegLocation(), reg, num, op);
                } else {
                    Reg reg1 = findRega();
                    generate_reg_num_cal(reg1, reg, num, op);
                    insert(resultSymbal, reg1);
                }
            } else {
                MipsSymbal op1Symbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(op1);
                MipsSymbal op2Symbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(op2);
                Reg reg1 = extract(op1Symbal);
                Reg reg2 = extract(op2Symbal);
                if (resultSymbal.onReg()) {
                    generate_reg_reg_cal(resultSymbal.getRegLocation(), reg1, reg2, op);
                } else {
                    Reg reg = findRega();
                    generate_reg_reg_cal(reg, reg1, reg2, op);
                    insert(resultSymbal, reg);
                }
            }
        }
        Grf.releaseAll();
    }

    public void generate_call(CallRP callRP, int i) {
        String returns = callRP.getReturns();
        String name = callRP.getName();
        ArrayList<String> paras = callRP.getParas();
        int paranum = paras.size();
        int depth = (paranum + 15) * 4;
        int up = 0;
        ADDI addi = new ADDI(Grf.sp, Grf.sp, -depth);
        MipsTable.nowfunction.addState(addi);
        for (String para: paras) {
            String paraName = getParaName(para);
            MipsSymbal paraSymbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(paraName);
            if (paraSymbal.getVtype().equals(Vtype.INT)) {
                Reg reg = extract_depth(paraSymbal, depth);
                SW sw = new SW(reg, String.valueOf(up), Grf.sp);
                MipsTable.nowfunction.addState(sw);
            } else if (paraSymbal.getVtype().equals(Vtype.INT1)) {
                if (paraSymbal.onStack()) {
                    int offset = paraSymbal.getSpLocation() + depth;
                    Reg reg = findRega();
                    ADDI addi1 = new ADDI(reg, Grf.sp, offset);
                    SW sw = new SW(reg, String.valueOf(up), Grf.sp);
                    MipsTable.nowfunction.addState(addi1);
                    MipsTable.nowfunction.addState(sw);
                } else if (paraSymbal.onData()) {
                    Space space = paraSymbal.getSpaceLocation();
                    Reg reg = findRega();
                    LA la = new LA(reg, space.getName());
                    SW sw = new SW(reg, String.valueOf(up), Grf.sp);
                    MipsTable.nowfunction.addState(la);
                    MipsTable.nowfunction.addState(sw);
                } else if (paraSymbal.onDirect()) {
                    int direct = paraSymbal.getDirectLocation() + depth;
                    Reg reg = findRega();
                    LW lw = new LW(reg, String.valueOf(direct), Grf.sp);
                    SW sw = new SW(reg, String.valueOf(up), Grf.sp);
                    MipsTable.nowfunction.addState(lw);
                    MipsTable.nowfunction.addState(sw);
                }
            } else if (paraSymbal.getVtype().equals(Vtype.INT2)) {
                if (para.contains("@")) {
                    String index = getParaIndex(para);
                    MipsSymbal symbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(index);
                    Reg reg = extract_index(symbal);
                    if (paraSymbal.onStack()) {
                        int offset = paraSymbal.getSpLocation() + depth;
                        Reg reg1 = findRega();
                        ADD add = new ADD(reg1, Grf.sp, reg);
                        ADDI addi1 = new ADDI(reg1, reg1, offset);
                        SW sw = new SW(reg1, String.valueOf(up), Grf.sp);
                        MipsTable.nowfunction.addState(add);
                        MipsTable.nowfunction.addState(addi1);
                        MipsTable.nowfunction.addState(sw);
                    } else if (paraSymbal.onData()) {
                        Space space = paraSymbal.getSpaceLocation();
                        LA la = new LA(Grf.v0, space.getName());
                        Reg reg1 = findRega();
                        ADD add = new ADD(reg1, Grf.v0, reg);
                        SW sw = new SW(reg1, String.valueOf(up), Grf.sp);
                        MipsTable.nowfunction.addState(la);
                        MipsTable.nowfunction.addState(add);
                        MipsTable.nowfunction.addState(sw);
                    } else if (paraSymbal.onDirect()) {
                        int direct = paraSymbal.getDirectLocation() + depth;
                        Reg reg1 = findRega();
                        int len2 = paraSymbal.getLen2();
                        LW lw = new LW(reg1, String.valueOf(direct), Grf.sp);
                        ADD add = new ADD(reg1, reg1, reg);
                        SW sw = new SW(reg1, String.valueOf(up), Grf.sp);
                        MipsTable.nowfunction.addState(lw);
                        MipsTable.nowfunction.addState(add);
                        MipsTable.nowfunction.addState(sw);
                    }
                } else {
                    if (paraSymbal.onStack()) {
                        int offset = paraSymbal.getSpLocation() + depth;
                        Reg reg = findRega();
                        ADDI addi1 = new ADDI(reg, Grf.sp, offset);
                        SW sw = new SW(reg, String.valueOf(up), Grf.sp);
                        MipsTable.nowfunction.addState(addi1);
                        MipsTable.nowfunction.addState(sw);
                    } else if (paraSymbal.onData()) {
                        Space space = paraSymbal.getSpaceLocation();
                        Reg reg = findRega();
                        LA la = new LA(reg, space.getName());
                        SW sw = new SW(reg, String.valueOf(up), Grf.sp);
                        MipsTable.nowfunction.addState(la);
                        MipsTable.nowfunction.addState(sw);
                    } else if (paraSymbal.onDirect()) {
                        int direct = paraSymbal.getDirectLocation() + depth;
                        Reg reg = findRega();
                        LW lw = new LW(reg, String.valueOf(direct), Grf.sp);
                        SW sw = new SW(reg, String.valueOf(up), Grf.sp);
                        MipsTable.nowfunction.addState(lw);
                        MipsTable.nowfunction.addState(sw);
                    }
                }
            }
            up += 4;
            Grf.releaseAll();
        }
        SW sw = new SW(Grf.ra, String.valueOf(up), Grf.sp);
        MipsTable.nowfunction.addState(sw);
        up += 4;
        for(Reg treg: Grf.regs_temp) {
            SW sw1 = new SW(treg, String.valueOf(up), Grf.sp);
            MipsTable.nowfunction.addState(sw1);
            up += 4;
        }
        for(Reg areg: Grf.regs_attr) {
            SW sw1 = new SW(areg, String.valueOf(up), Grf.sp);
            MipsTable.nowfunction.addState(sw1);
            up += 4;
        }
        MipsSymbal func = (MipsSymbal) MipsTable.mipsTable.findSymbal(name);
        String location = func.getName();
        JAL jal = new JAL(location);
        MipsTable.nowfunction.addState(jal);
        up = depth - 60;
        LW lw = new LW(Grf.ra, String.valueOf(up), Grf.sp);
        MipsTable.nowfunction.addState(lw);
        up += 4;
        for(Reg treg: Grf.regs_temp) {
            LW lw1 = new LW(treg, String.valueOf(up), Grf.sp);
            MipsTable.nowfunction.addState(lw1);
            up += 4;
        }
        for(Reg areg: Grf.regs_attr) {
            LW lw1 = new LW(areg, String.valueOf(up), Grf.sp);
            MipsTable.nowfunction.addState(lw1);
            up += 4;
        }
        ADDI addi1 = new ADDI(Grf.sp, Grf.sp, depth);
        MipsTable.nowfunction.addState(addi1);
        if (returns != null) {
            String returnname = getParaName(returns);
            String index = getParaIndex(returns);
            if (!MipsTable.mipsTable.checkSymbals(returnname)) {
                MipsSymbal resultSymbal = new MipsSymbal(returnname, Vtype.INT);
                if (returnname.charAt(0) == '$') {
                    int number = Integer.parseInt(returnname.substring(4));
                    if (number < 10) {
                        resultSymbal.setRegLocation(Grf.regs_temp.get(number));
                    } else {
                        resultSymbal.setSpLocation(stackindex);
                        pushStack();
                    }
                    MipsTable.mipsTable.addSymbal(resultSymbal);
                } else {
                    System.out.println(returnname);
                    System.out.println("ERROR_a");
                }
            }
            MipsSymbal symbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(returnname);
            insert(symbal, Grf.v0, index);
        }
        Grf.releaseAll();
    }

    public void generate_getint(GetintRP getintRP, int i) {
        LI li = new LI(Grf.v0, 5);
        MipsTable.nowfunction.addState(li);
        SYSCALL syscall = new SYSCALL();
        MipsTable.nowfunction.addState(syscall);
        String name = getintRP.getName();
        if (!MipsTable.mipsTable.checkSymbals(name)) {
            MipsSymbal resultSymbal = new MipsSymbal(name, Vtype.INT);
            if (name.charAt(0) == '$') {
                int number = Integer.parseInt(name.substring(4));
                if (number < 10) {
                    resultSymbal.setRegLocation(Grf.regs_temp.get(number));
                } else {
                    resultSymbal.setSpLocation(stackindex);
                    pushStack();
                }
                MipsTable.mipsTable.addSymbal(resultSymbal);
            } else {
                System.out.println(name);
                System.out.println("ERROR_a");
            }
        }
        MipsSymbal mipsSymbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(name);
        insert(mipsSymbal, Grf.v0);
    }

    public void generate_printf(PrintRP printRP, int i) {
        String content = printRP.getFormatstring();
        ArrayList<String> paras = printRP.getParas();
        ArrayList<String> units = sapaString(content);
        int paraindex = 0;
        for (String unit: units) {
            if (unit.equals("%d")) {
                String para = paras.get(paraindex);
                MipsSymbal symbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(para);
                Reg reg = extract(symbal);
                if (!reg.equals(Grf.a0)) {
                    MOVE move = new MOVE(Grf.a0, reg);
                    MipsTable.nowfunction.addState(move);
                }
                LI li = new LI(Grf.v0, 1);
                MipsTable.nowfunction.addState(li);
                SYSCALL syscall = new SYSCALL();
                MipsTable.nowfunction.addState(syscall);
                paraindex++;
                Grf.releaseAll();
            } else {
                String asname = "str" + MipsTable.strnum;
                Asciiz asciiz = new Asciiz(asname, "\"" + unit + "\"");
                MipsTable._datastr.add(asciiz);
                LA la = new LA(Grf.a0, asname);
                MipsTable.nowfunction.addState(la);
                LI li = new LI(Grf.v0, 4);
                MipsTable.nowfunction.addState(li);
                SYSCALL syscall = new SYSCALL();
                MipsTable.nowfunction.addState(syscall);
                MipsTable.strnum++;
            }
        }
    }

    public void generate_return(ReturnRP returnRP, int i) {
        String ret = returnRP.getRet();
        if (ret != null) {
            MipsSymbal symbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(ret);
            Reg reg = extract(symbal);
            MOVE move = new MOVE(Grf.v0, reg);
            MipsTable.nowfunction.addState(move);
        }
        if (MipsTable.nowfunction != MipsTable.meanfunction) {
            J j = new J(MipsTable.nowfunction.getName() + "_end");
            MipsTable.nowfunction.addState(j);
        }
    }

    public void generate_posi(String op1, String result) {
        MipsSymbal resultSymbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(result);
        if (isNumber(op1)) {
            int num = Integer.parseInt(op1);
            assign_symbal_num(resultSymbal, num, null);
        } else {
            MipsSymbal sourceSymbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(op1);
            assign_symbal_symbal(resultSymbal, sourceSymbal, null, null);
        }
    }

    public void generate_nega(String op1, String result) {
        MipsSymbal resultSymbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(result);
        if (isNumber(op1)) {
            int num = Integer.parseInt(op1);
            assign_symbal_num(resultSymbal, -num, null);
        } else {
            MipsSymbal sourceSymbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(op1);
            Reg reg = extract(sourceSymbal);
            if (resultSymbal.onReg()) {
                generate_reg_reg_cal(resultSymbal.getRegLocation(), Grf.zero, reg, "-");
            } else {
                Reg reg1 = findRega();
                generate_reg_reg_cal(reg1, Grf.zero, reg, "-");
                insert(resultSymbal, reg1);
            }
        }
    }

    public void generate_not(String op1, String result) {
        MipsSymbal resultSymbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(result);
        if (isNumber(op1)) {
            int num = Integer.parseInt(op1);
            if (num == 0) {
                assign_symbal_num(resultSymbal, 1, null);
            } else {
                assign_symbal_num(resultSymbal, 0, null);
            }
        } else {
            MipsSymbal sourceSymabal = (MipsSymbal) MipsTable.mipsTable.findSymbal(op1);
            Reg reg = extract(sourceSymabal);
            if (resultSymbal.onReg()) {
                SEQ seq = new SEQ(resultSymbal.getRegLocation(), reg, 0);
                MipsTable.nowfunction.addState(seq);
            } else {
                Reg reg1 = findRega();
                SNE sne = new SNE(reg1, reg, 0);
                MipsTable.nowfunction.addState(sne);
                insert(resultSymbal, reg1);
            }
        }
    }

    public void generate_reg_reg_cal(Reg rd, Reg rs, Reg rt, String op) {
        if (op.equals("+")) {
            ADD add = new ADD(rd, rs, rt);
            MipsTable.nowfunction.addState(add);
        } else if (op.equals("-")) {
            SUB sub = new SUB(rd, rs, rt);
            MipsTable.nowfunction.addState(sub);
        } else if (op.equals("*")) {
            MULT mult = new MULT(rs, rt);
            MFLO mflo = new MFLO(rd);
            MipsTable.nowfunction.addState(mult);
            MipsTable.nowfunction.addState(mflo);
        } else if (op.equals("/")) {
            DIV div = new DIV(rs, rt);
            MFLO mflo = new MFLO(rd);
            MipsTable.nowfunction.addState(div);
            MipsTable.nowfunction.addState(mflo);
        } else if (op.equals("%")) {
            DIV div = new DIV(rs, rt);
            MFHI mfhi = new MFHI(rd);
            MipsTable.nowfunction.addState(div);
            MipsTable.nowfunction.addState(mfhi);
        } else if (op.equals(">")) {
            SGT sgt = new SGT(rd, rs, rt);
            MipsTable.nowfunction.addState(sgt);
        } else if (op.equals("<")) {
            SLT slt = new SLT(rd, rs, rt);
            MipsTable.nowfunction.addState(slt);
        } else if (op.equals(">=")) {
            SGE sge = new SGE(rd, rs, rt);
            MipsTable.nowfunction.addState(sge);
        } else if (op.equals("<=")) {
            SLE sle = new SLE(rd, rs, rt);
            MipsTable.nowfunction.addState(sle);
        } else if (op.equals("==")) {
            SEQ seq = new SEQ(rd, rs, rt);
            MipsTable.nowfunction.addState(seq);
        } else if (op.equals("!=")) {
            SNE sne = new SNE(rd, rs, rt);
            MipsTable.nowfunction.addState(sne);
        }
    }

    public void generate_reg_num_cal(Reg rd, Reg rs, int num, String op) {
        if (op.equals("+")) {
            ADDI addi = new ADDI(rd, rs, num);
            MipsTable.nowfunction.addState(addi);
        } else if (op.equals("-")) {
            ADDI addi = new ADDI(rd, rs, -num);
            MipsTable.nowfunction.addState(addi);
        } else if (op.equals("*")) {
            LI li = new LI(Grf.v0, num);
            MULT mult = new MULT(rs, Grf.v0);
            MFLO mflo = new MFLO(rd);
            MipsTable.nowfunction.addState(li);
            MipsTable.nowfunction.addState(mult);
            MipsTable.nowfunction.addState(mflo);
        } else if (op.equals("/")) {
            LI li = new LI(Grf.v0, num);
            DIV div = new DIV(rs, Grf.v0);
            MFLO mflo = new MFLO(rd);
            MipsTable.nowfunction.addState(li);
            MipsTable.nowfunction.addState(div);
            MipsTable.nowfunction.addState(mflo);
        } else if (op.equals("%")) {
            LI li = new LI(Grf.v0, num);
            DIV div = new DIV(rs, Grf.v0);
            MFHI mfhi = new MFHI(rd);
            MipsTable.nowfunction.addState(li);
            MipsTable.nowfunction.addState(div);
            MipsTable.nowfunction.addState(mfhi);
        } else if (op.equals(">")) {
            SGT sgt = new SGT(rd, rs, num);
            MipsTable.nowfunction.addState(sgt);
        } else if (op.equals("<")) {
            SLTI slti = new SLTI(rd, rs, num);
            MipsTable.nowfunction.addState(slti);
        } else if (op.equals(">=")) {
            SGE sge = new SGE(rd, rs, num);
            MipsTable.nowfunction.addState(sge);
        } else if (op.equals("<=")) {
            SLE sle = new SLE(rd, rs, num);
            MipsTable.nowfunction.addState(sle);
        } else if (op.equals("==")) {
            SEQ seq = new SEQ(rd, rs, num);
            MipsTable.nowfunction.addState(seq);
        } else if (op.equals("!=")) {
            SNE sne = new SNE(rd, rs, num);
            MipsTable.nowfunction.addState(sne);
        }
    }

    public void generate_num_reg_cal(Reg rd, int num, Reg rs, String op) {
        if (op.equals("+")) {
            generate_reg_num_cal(rd, rs, num, op);
        } else if (op.equals("-")) {
            LI li = new LI(Grf.v0, num);
            SUB sub = new SUB(rd, Grf.v0, rs);
            MipsTable.nowfunction.addState(li);
            MipsTable.nowfunction.addState(sub);
        } else if (op.equals("*")) {
            generate_reg_num_cal(rd, rs, num, op);
        } else if (op.equals("/")) {
            LI li = new LI(Grf.v0, num);
            DIV div = new DIV(Grf.v0, rs);
            MFLO mflo = new MFLO(rd);
            MipsTable.nowfunction.addState(li);
            MipsTable.nowfunction.addState(div);
            MipsTable.nowfunction.addState(mflo);
        } else if (op.equals("%")) {
            LI li = new LI(Grf.v0, num);
            DIV div = new DIV(Grf.v0, rs);
            MFHI mfhi = new MFHI(rd);
            MipsTable.nowfunction.addState(li);
            MipsTable.nowfunction.addState(div);
            MipsTable.nowfunction.addState(mfhi);
        } else if (op.equals(">")) {
            generate_reg_num_cal(rd, rs, num, "<");
        } else if (op.equals("<")) {
            generate_reg_num_cal(rd, rs, num, ">");
        } else if (op.equals(">=")) {
            generate_reg_num_cal(rd, rs, num, "<=");
        } else if (op.equals("<=")) {
            generate_reg_num_cal(rd, rs, num, ">=");
        } else if (op.equals("==")) {
            generate_reg_num_cal(rd, rs, num, op);
        } else if (op.equals("!=")) {
            generate_reg_num_cal(rd, rs, num, op);
        }
    }

    public void assign_symbal_num(MipsSymbal symbal, int num, String index) {
        if (symbal.onReg()) {
            Reg reg = symbal.getRegLocation();
            LI li = new LI(reg, num);
            MipsTable.nowfunction.addState(li);
        } else {
            Reg reg = findRega();
            LI li = new LI(reg, num);
            MipsTable.nowfunction.addState(li);
            insert(symbal, reg, index);
        }
        Grf.releaseAll();
    }

    public void assign_symbal_symbal(MipsSymbal target, MipsSymbal source, String index1, String index2) {
        Reg reg = extract(source, index2);
        insert(target, reg, index1);
        Grf.releaseAll();
    }

    public Reg extract_index(MipsSymbal symbal) {
        Reg reg = findRega();
        if (symbal.onReg()) {
            Reg reg1 = symbal.getRegLocation();
            SLL sll = new SLL(reg, reg1, 2);
            MipsTable.nowfunction.addState(sll);
        } else if (symbal.onData()) {
            Space space = symbal.getSpaceLocation();
            String label = space.getName();
            LW lw = new LW(reg, label, Grf.zero);
            SLL sll = new SLL(reg, reg, 2);
            MipsTable.nowfunction.addState(lw);
            MipsTable.nowfunction.addState(sll);
        } else if (symbal.onStack()) {
            int offset = symbal.getSpLocation();
            LW lw = new LW(reg, String.valueOf(offset), Grf.sp);
            SLL sll = new SLL(reg, reg, 2);
            MipsTable.nowfunction.addState(lw);
            MipsTable.nowfunction.addState(sll);
        }
        return reg;
    }

    public Reg extract_index(String index) {
        if (isNumber(index)) {
            Reg reg = findRega();
            LI li = new LI(reg, Integer.parseInt(index) * 4);
            MipsTable.nowfunction.addState(li);
            return reg;
        }
        MipsSymbal symbal = (MipsSymbal) MipsTable.mipsTable.findSymbal(index);
        return extract_index(symbal);
    }

    public Reg extract(MipsSymbal symbal, String index) {
        if (symbal.onData()) {
            Reg reg = findRega();
            Space space = symbal.getSpaceLocation();
            String label = space.getName();
            if (symbal.getVtype().equals(Vtype.INT)) {
                LW lw = new LW(reg, label, Grf.zero);
                MipsTable.nowfunction.addState(lw);
            } else {
                Reg reg1 = extract_index(index);
                LW lw = new LW(reg, label, reg1);
                MipsTable.nowfunction.addState(lw);
            }
            return reg;
        } else if (symbal.onStack()) {
            Reg reg = findRega();
            int offset = symbal.getSpLocation();
            if (symbal.getVtype().equals(Vtype.INT)) {
                LW lw = new LW(reg, String.valueOf(offset), Grf.sp);
                MipsTable.nowfunction.addState(lw);
            } else {
                Reg reg1 = extract_index(index);
                ADD add = new ADD(reg1, reg1, Grf.sp);
                LW lw = new LW(reg, String.valueOf(offset), reg1);
                MipsTable.nowfunction.addState(add);
                MipsTable.nowfunction.addState(lw);
            }
            return reg;
        } else if (symbal.onReg()) {
            return symbal.getRegLocation();
        } else if (symbal.onDirect()) {
            Reg reg = findRega();
            int direct = symbal.getDirectLocation();
            Reg reg1 = extract_index(index);
            LW lw = new LW(reg, String.valueOf(direct), Grf.sp);
            ADD add = new ADD(reg1, reg1, reg);
            LW lw1 = new LW(reg, "0", reg1);
            MipsTable.nowfunction.addState(lw);
            MipsTable.nowfunction.addState(add);
            MipsTable.nowfunction.addState(lw1);
            return reg;
        } else {
            return Grf.zero;
        }
    }

    public Reg extract(MipsSymbal symbal) {
        return extract(symbal, null);
    }

    public Reg extract_depth(MipsSymbal symbal, String index, int depth) {
        if (symbal.onStack()) {
            Reg reg = findRega();
            int offset = symbal.getSpLocation() + depth;
            if (symbal.getVtype().equals(Vtype.INT)) {
                LW lw = new LW(reg, String.valueOf(offset), Grf.sp);
                MipsTable.nowfunction.addState(lw);
            } else {
                Reg reg1 = extract_index(index);
                ADD add = new ADD(reg1, reg1, Grf.sp);
                LW lw = new LW(reg, String.valueOf(offset), reg1);
                MipsTable.nowfunction.addState(add);
                MipsTable.nowfunction.addState(lw);
            }
            return reg;
        } else if (symbal.onDirect()) {
            Reg reg = findRega();
            int direct = symbal.getDirectLocation() + depth;
            Reg reg1 = extract_index(index);
            LW lw = new LW(reg, String.valueOf(direct), Grf.sp);
            ADD add = new ADD(reg1, reg1, reg);
            LW lw1 = new LW(reg, "0", reg1);
            MipsTable.nowfunction.addState(lw);
            MipsTable.nowfunction.addState(add);
            MipsTable.nowfunction.addState(lw1);
            return reg;
        } else {
            return extract(symbal, index);
        }
    }

    public Reg extract_depth(MipsSymbal symbal, int depth) {
        return extract_depth(symbal, null, depth);
    }

    public void insert(MipsSymbal symbal, Reg reg) {
        insert(symbal, reg, null);
    }

    public void insert(MipsSymbal symbal, Reg reg, String index) {
        if (symbal.onReg()) {
            Reg reg1 = symbal.getRegLocation();
            MOVE move = new MOVE(reg1, reg);
            MipsTable.nowfunction.addState(move);
        } else if (symbal.onStack()) {
            int offset = symbal.getSpLocation();
            if (symbal.getVtype().equals(Vtype.INT)) {
                SW sw = new SW(reg, String.valueOf(offset), Grf.sp);
                MipsTable.nowfunction.addState(sw);
            } else {
                Reg reg1 = extract_index(index);
                ADD add = new ADD(reg1, reg1, Grf.sp);
                SW sw = new SW(reg, String.valueOf(offset), reg1);
                MipsTable.nowfunction.addState(add);
                MipsTable.nowfunction.addState(sw);
            }
        } else if (symbal.onData()) {
            Space space = symbal.getSpaceLocation();
            String label = space.getName();
            if (symbal.getVtype().equals(Vtype.INT)) {
                SW sw = new SW(reg, label, Grf.zero);
                MipsTable.nowfunction.addState(sw);
            } else {
                Reg reg1 = extract_index(index);
                SW sw = new SW(reg, label, reg1);
                MipsTable.nowfunction.addState(sw);
            }
        } else if (symbal.onDirect()) {
            int direct = symbal.getDirectLocation();
            Reg reg1 = extract_index(index);
            Reg reg2 = findRega();
            LW lw = new LW(reg2, String.valueOf(direct), Grf.sp);
            ADD add = new ADD(reg1, reg1, reg2);
            SW sw = new SW(reg, "0", reg1);
            MipsTable.nowfunction.addState(lw);
            MipsTable.nowfunction.addState(add);
            MipsTable.nowfunction.addState(sw);
        }
    }
}
